package snooker.scoreboard;

import java.util.LinkedList;
import java.util.List;

import android.util.Log;


public class Frame {
	
	private static final String LOG_TAG = "Frame";
	private int frameId;
	
	private int player1Score = 0;
	private int player2Score = 0;
	
	private int reds;
	private int maxReds;
	
	private int breakId;
	private int actPlayer;
	private boolean newBreak;
	
	private FrameHistory history;
	
	private boolean yellowOn = true;
	private boolean greenOn = true;
	private boolean brownOn = true;
	private boolean blueOn = true;
	private boolean pinkOn = true;
	private boolean blackOn = true;
	
	public Frame(int frameId, int maxReds) {
		history = new FrameHistory();
		Log.i("new Frame()", "frameId=" + this.frameId);
		Log.i("new Frame()", "maxReds=" + this.maxReds);
		this.frameId = frameId;
		this.reds = maxReds;
		this.maxReds = maxReds;
		this.breakId = 0;
		this.actPlayer = Match.PLAYER1;
		this.newBreak = true;
	}
	
	public Frame(int frameId, int maxReds, int player1Score, int player2Score, 
			int breakId, int actPlayer, FrameHistory history) {
		Log.i("Load new Frame()", "frameId=" + frameId);
		Log.i("Load new Frame()", "maxReds=" + maxReds);
		this.frameId = frameId;
		this.reds = maxReds;
		this.maxReds = maxReds;
		this.breakId = breakId;
		this.actPlayer = actPlayer;
		this.newBreak = true;
		this.player1Score = player1Score;
		this.player2Score = player2Score;
		this.history = history;
	}
	
	public int getReds() {
		return reds;
	}
	
	public int getFrameId() {
		return frameId;
	}
	
	public int getPlayer1Score() {
		return player1Score;
	}
	
	public int getPlayer2Score() {
		return player2Score;
	}
	
	public void selectPlayer(int player) {
		if (player != actPlayer && (player == Match.PLAYER1 || player == Match.PLAYER2)) {
			actPlayer = player;
			newBreak = true;
		}
	}
	
	public void unintendedRed(boolean isLuckyShot) {
		if (reds > 0) {
			reds--;
			history.addState(actPlayer, frameId, Type.UnintededRed, 0, Ball.Red, 0, player1Score, player2Score, reds, isLuckyShot);
		}
	}
	
	public void setScore(final Ball ball, boolean fault, boolean isLuckyShot) {
		boolean isFault = fault;
		int score = 0;
		Type type = Type.Pot;
		Ball actBall = ball;
		switch (ball) {
		case White:
			isFault = true;
			score = ball.getFaultValue();
			break;
		case Red:
			if (isFault) {
				score = ball.getFaultValue();
			} else {
				score = ball.getValue();
			}
			if (reds > 0) {
				reds--;
			} else {
				score = 0;
			}
			break;
		case FreeBall:
			if (reds == 0) {
				if (isOn(Ball.Yellow)) {
					actBall = Ball.Yellow;
				} else if (isOn(Ball.Green)) {
					actBall = Ball.Green;
				} else if (isOn(Ball.Brown)) {
					actBall = Ball.Brown;
				} else if (isOn(Ball.Blue)) {
					actBall = Ball.Blue;
				} else if (isOn(Ball.Pink)) {
					actBall = Ball.Pink;
				} else {
					score = 0;
					break;
				}
			} else {
				actBall = Ball.Red;
			}
			score = actBall.getValue();
			type = Type.Freeball;
			break;
		default:
			if (isFault) {
				score = ball.getFaultValue();
			} else {
				FrameState state = history.getLastState();
				if (state == null) {
					score = ball.getFaultValue();
					isFault = true;
				} else if (!newBreak && Ball.Red.equals(state.getBall())) {
					score = ball.getValue();
				} else if (reds == 0 && isOn(ball) && canPot(ball)) {
					if (!(ball == Ball.Black && Math.abs(player1Score - player2Score) == Ball.Black.getValue())) {
						setOn(ball, false);
					}
					score = ball.getValue();
				} else if (reds == 0 && !isOn(ball)) {
					
				} else {
					score = ball.getFaultValue();
					isFault = true;
				}
			}
			break;
		}
		if (score > 0) {
			if (!isFault && newBreak) {
				newBreak = false;
				breakId++;
			}
			if (isFault) {
				type = Type.Foul;
			}
			increment(actPlayer, score, isFault);
			history.addState(actPlayer, frameId, type, breakId, actBall, score, 
					player1Score, player2Score, reds, isLuckyShot && !isFault);
		}
		if (isFault) {
			// Switch players
			selectPlayer(Match.PLAYER1 == actPlayer ? Match.PLAYER2 : Match.PLAYER1);
		}
	}
	
	private void increment(int player, int score, boolean fault) {
		switch (player) {
		case Match.PLAYER1:
			if (fault) {
				player2Score += score;
			} else
				player1Score += score;
			break;
		case Match.PLAYER2:
			if (fault) {
				player1Score += score;
			} else
				player2Score += score;
			break;
		}
		
	}
	
	public void undo() {
		Log.i(LOG_TAG, "<<<<<<<<<<<<<<<<<<<< UNDO >>>>>>>>>>>>>>>>>>>>>>");
		FrameState removeState = history.getLastState();
		history.undo();
		FrameState restoreState = history.getLastState();
		if (restoreState == null) {
			player1Score = 0;
			player2Score = 0;
			reds = maxReds;
			breakId = 0; 
			newBreak = true;
			actPlayer = Match.PLAYER1;
		} else { 
			Log.i(LOG_TAG, "Restore state: " + restoreState.toString());
			Log.i(LOG_TAG, "Remove state: " + removeState.toString());
			player1Score = restoreState.getPlayer1Score();
			player2Score = restoreState.getPlayer2Score();
			actPlayer = restoreState.getPlayerId();  
			reds = restoreState.getReds();
			breakId = restoreState.getBreakId();
			setOn(removeState.getBall(),true);
			FrameState last = history.getLastState();
			newBreak = true;
			if (last != null && !last.isFoul() && last.getBreakId() == breakId) {
				newBreak = false;
			}
		}
	}
	
	public FrameHistory getHistory() {
		return history;
	}
	
	public int getActPlayer() {
		return actPlayer;
	}
	
	public String getInfo() {
		String info = "";
		int remaining = getRemaining();
		int diff = Math.abs(player1Score - player2Score);
		if (diff == 0) {
			info = "0 difference";
		} else if (actPlayer == Match.PLAYER1) {
			if (player1Score > player2Score) {
				info = diff + " ahead";
			} else {
				info = diff + " behind";
			}
		} else if (actPlayer == Match.PLAYER2) {
			if (player1Score > player2Score) {
				info = diff + " behind";
			} else {
				info = diff + " ahead";
			}
		}
		info += ", " + remaining + " remaining";
		return info;
	}
	
	public BreakInfo getBreakInfo() {
		BreakInfo info = null;
		List<Ball> balls = new LinkedList<Ball>();
		Log.i(LOG_TAG, "breakId=" + breakId);
		int breakCount = 0;
		if (!newBreak) {
			for (int i = history.getHistory().size() - 1; i >= 0; i--) {
				FrameState state = history.getHistory().get(i);
				if (!state.isFoul() && breakId != 0 && breakId == state.getBreakId()) {
					//info =  " " + state.getBall() + ("".equals(info) ? "" :  ", " + info);
					balls.add(state.getBall());
					breakCount += state.getScore();
				} else {
					break;
				}
			}
		}
		Log.i(LOG_TAG, "Break count=" + breakCount);
		info = new BreakInfo(breakCount, balls);
		return info;
	}
	
	public boolean isOn(Ball ball) {
		boolean isOn = false;
		switch (ball) {
		case Yellow:
			isOn = yellowOn;
			break;
		case Green:
			isOn = greenOn;
			break;
		case Brown:
			isOn = brownOn;
			break;
		case Blue:
			isOn = blueOn;
			break;
		case Pink:
			isOn = pinkOn;
			break;
		case Black:
			isOn = blackOn;
			break;
		case FreeBall:
			FrameState state = history.getLastState();
			if (state == null) {
				isOn = false;
			} else {
				if (state.isFoul() && pinkOn) {
					isOn = true;
				}
			}
			break;
		}
		return isOn;
	}
	
	private boolean canPot(Ball ball) {
		boolean canPot = false;
		switch (ball) {
		case Yellow:
			canPot = true;
			break;
		case Green:
			canPot = !isOn(Ball.Yellow);
			break;
		case Brown:
			canPot = !isOn(Ball.Green);
			break;
		case Blue:
			canPot = !isOn(Ball.Brown);
			break;
		case Pink:
			canPot = !isOn(Ball.Blue);
			break;
		case Black:
			canPot = !isOn(Ball.Pink);
			break;
		case FreeBall:
			canPot = !isOn(Ball.FreeBall);
			break;
		}
		return canPot;
	}
	
	private void setOn(Ball ball, boolean on){
		switch (ball) {
		case Yellow:
			yellowOn = on;
			break;
		case Green:
			greenOn = on;
			break;
		case Brown:
			brownOn = on;
			break;
		case Blue:
			blueOn = on;
			break;
		case Pink:
			pinkOn = on;
			break;
		case Black:
			blackOn = on;
			break;
		}
	}
	
	private int getRemaining() {
		int remaining = 0;
		if (reds > 0) {
			remaining = reds * 7 + reds + 27;
		} else {
			if (!newBreak && Ball.Red.equals(history.getLastState().getBall())) {
				remaining = 34;
			} else if (yellowOn) {
				remaining = 27;
			} else if (greenOn) {
				remaining = 25;
			} else if (brownOn) {
				remaining = 22;
			} else if (blueOn) {
				remaining = 18;
			} else if (pinkOn) {
				remaining = 13;
			} else if (blackOn) {
				remaining = 7;
			} else if (player1Score == player2Score) {
				remaining = 7;
			}
		}
		return remaining;
	}
	
	public void debug() {
		Log.i(LOG_TAG, "frameId=" + frameId);
		history.debug();
	}
}
